package cn.ultrarisk.scadmin.manager.biz.common.service;

import cn.ultrarisk.mybatis.extension.query.Updater;
import cn.ultrarisk.scadmin.manager.biz.common.context.OperationType;
import cn.ultrarisk.scadmin.manager.biz.common.dao.SchedulerJobDAO;
import cn.ultrarisk.scadmin.manager.biz.common.entity.SchedulerJob;
import cn.ultrarisk.scadmin.manager.biz.common.factory.QuartzJobFactory;
import cn.ultrarisk.scadmin.manager.biz.common.valid.SchedulerJobValidator;
import cn.ultrarisk.scadmin.manager.exception.BizCheckedException;
import cn.ultrarisk.scadmin.manager.support.SpringContextHolder;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 调度管理数据操作类
 *
 * @author sevendlong(lin23871@163.com)
 * @date 2014/3/28
 * @since V1.0
 */
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class SchedulerJobService {

    @Autowired
    private SchedulerJobDAO schedulerJobDAO;
    @Autowired
    private Scheduler scheduler;
    @Autowired
    private SchedulerJobValidator validator;
    @Autowired
    private OperationLogService operationLogService;

    /**
     * 创建任务
     *
     * @query schedulerJob
     * @throws Exception
     */
    public void create(SchedulerJob schedulerJob) throws Exception {
        validator.validName(schedulerJob.getName());
        validator.validBeanMethod(schedulerJob.getBean(), schedulerJob.getMethod());
        validator.validExpression(schedulerJob.getExpression());
        if (schedulerJob.getExecCount() == null) {
            schedulerJob.setExecCount(0L);
        }
        schedulerJobDAO.create(schedulerJob);
        if (SchedulerJob.STATAUS_ENABLED == schedulerJob.getStatus()) {
            run(schedulerJob);
        }
        operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_CREATE, schedulerJob.getId());
    }

    /**
     * 更新调度
     *
     * @query id
     * @query expression
     * @query remark
     * @throws Exception
     */
    public void update(Integer id, String expression, String remark) throws Exception {
        validator.validExpression(expression);
        schedulerJobDAO.update(id, Updater.update(SchedulerJob.EXPRESSION, expression).addField(SchedulerJob.REMARK, remark));
        SchedulerJob schedulerJob = schedulerJobDAO.get(id);
        run(schedulerJob);
        operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_UPDATE, id);
    }

    /**
     * 删除任务
     *
     * @query id
     * @throws Exception
     */
    public void delete(Integer id) throws Exception {
        SchedulerJob schedulerJob = schedulerJobDAO.get(id);
        if (schedulerJob != null) {
            scheduler.deleteJob(JobKey.jobKey(schedulerJob.getName()));
            operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_DELETE, id);
            schedulerJobDAO.delete(id);
        }
    }

    /**
     * 禁用
     *
     * @query id
     * @throws Exception
     */
    public void disable(Integer id) throws Exception {
        SchedulerJob schedulerJob = schedulerJobDAO.get(id);
        if (schedulerJob != null) {
            stop(schedulerJob.getName());
            if (SchedulerJob.STATAUS_DISABLED != schedulerJob.getStatus()) {
                Updater updater = Updater.update(SchedulerJob.STATUS, SchedulerJob.STATAUS_DISABLED);
                schedulerJobDAO.update(id, updater);
                operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_DISABLE, id);
            }
        }
    }

    /**
     * 启用
     *
     * @query id
     * @throws Exception
     */
    public void enable(Integer id) throws Exception {
        SchedulerJob schedulerJob = schedulerJobDAO.get(id);
        if (schedulerJob != null) {
            run(schedulerJob);
            if (SchedulerJob.STATAUS_ENABLED != schedulerJob.getStatus()) {
                Updater updater = Updater.update(SchedulerJob.STATUS, SchedulerJob.STATAUS_ENABLED);
                schedulerJobDAO.update(id, updater);
                operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_ENABLE, id);
            }
        }
    }

    public void execute(Integer id) throws Exception {
        SchedulerJob schedulerJob = schedulerJobDAO.get(id);
        if (schedulerJob != null && schedulerJob.getStatus() == SchedulerJob.STATAUS_ENABLED) {
            Object bean = SpringContextHolder.getBean(Class.forName(schedulerJob.getBean()));
            Method method = bean.getClass().getMethod(schedulerJob.getMethod());
            long start = System.currentTimeMillis();
            method.invoke(bean);
            long end = System.currentTimeMillis();
            addExecCountAndTime(schedulerJob.getId(), (end - start));
        } else {
            throw new BizCheckedException("the schedulerJob must be enabled");
        }
    }

    /**
     * 运行任务
     *
     * @query schedulerJob
     * @throws Exception
     */
    private void run(SchedulerJob schedulerJob) throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(schedulerJob.getName());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withIdentity(schedulerJob.getName()).build();
            jobDetail.getJobDataMap().put(QuartzJobFactory.SCHEDULE_JOB_NAME, schedulerJob);

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(schedulerJob.getExpression());
            //按新的cronExpression表达式构建一个新的trigger
            trigger = TriggerBuilder.newTrigger().withIdentity(schedulerJob.getName()).withSchedule(scheduleBuilder).build();
            //设置job执行
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            //按新的trigger重新设置job执行
            JobKey jobKey = JobKey.jobKey(schedulerJob.getName());
            scheduler.triggerJob(jobKey);
        }
    }

    /**
     * 暂停任务
     *
     * @query name
     * @throws Exception
     */
    private void stop(String name) throws Exception {
        scheduler.deleteJob(JobKey.jobKey(name));
    }

    /**
     * 与数据库同步调度任务
     */
    public void sync() throws Exception {
        List<SchedulerJob> schedulerJobs = schedulerJobDAO.findList(SchedulerJob.STATAUS_ENABLED);
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.anyJobGroup());

        Map<String, SchedulerJob> schedulerJobMap = new HashMap<String, SchedulerJob>();
        for (SchedulerJob schedulerJob : schedulerJobs) {
            schedulerJobMap.put(schedulerJob.getName(), schedulerJob);
        }
        Map<String, JobKey> jobKeyMap = new HashMap<String, JobKey>();
        for (JobKey jobKey : jobKeys) {
            jobKeyMap.put(jobKey.getName(), jobKey);
        }

        // 添加数据库的任务到调度列表
        for (SchedulerJob schedulerJob : schedulerJobs) {
            if (!jobKeyMap.containsKey(schedulerJob.getName())) {
                run(schedulerJob);
            }
        }

        // 删除数据库中不存在的调度任务
        for (JobKey jobKey : jobKeys) {
            if (!schedulerJobMap.containsKey(jobKey.getName())) {
                stop(jobKey.getName());
            }
        }
        operationLogService.createSchedulerJobLog(OperationType.SCHEDULER_SYNC);
    }

    public void addExecCountAndTime(Integer id, long time) {
        SchedulerJob schedulerJob = schedulerJobDAO.get(id, SchedulerJob.EXEC_COUNT, SchedulerJob.EXEC_TIME);
        Double execTime = (schedulerJob.getExecTime() * schedulerJob.getExecCount() + time) * 1.0 / (schedulerJob.getExecCount() + 1);
        schedulerJobDAO.update(id, Updater.update(SchedulerJob.EXEC_COUNT, Updater.Opt.ADD, 1).addField(SchedulerJob.EXEC_TIME, execTime));
    }
}